#ifndef _EQTEST_OBJ_LOADER_H
#define _EQTEST_OBJ_LOADER_H

#include <map>
#include <vector>
#include <string>

namespace eqtest
{
    namespace obj_loader
    {
        struct Vertex
        {
            Vertex(float x, float y, float z, float w = 1.0f) : x(x), y(y), z(z), w(w) {}
            
            float x, y, z, w;
        };
        
        struct TexCoord
        {
            TexCoord(float s, float t, float p = 0.0f) : s(s), t(t), p(p) {}
            
            float s, t, p;
        };
        
        struct Normal
        {
            Normal(float x, float y, float z) : x(x), y(y), z(z) {}
            
            float x, y, z;
        };
        
        struct VertexAttrId
        {
            VertexAttrId(int vertexId, int texCoordId = 0, int normalId = 0)
                : vertexId(vertexId - 1), texCoordId(texCoordId - 1), normalId(normalId - 1) {}
            
            int vertexId;
            int texCoordId;
            int normalId;
        };
        
        struct Face
        {
            template <typename Iter>
            Face(Iter const& begin, Iter const& end) : vertexAttrIds(begin, end) {}
            
            std::vector<VertexAttrId> vertexAttrIds;
        };
        
        struct MtlLib
        {
            MtlLib(std::string const& fileName) : fileName(fileName) {}
            
            std::string fileName;
        };
        
        struct Object
        {
            Object(std::string const& objectName) : objectName(objectName) {}
            
            std::string objectName;
        };
        
        struct Group
        {
            Group(std::string const& groupName) : groupName(groupName) {}
            
            std::string groupName;
        };
        
        struct Material
        {
            Material(std::string const& materialName) : materialName(materialName) {}
            
            std::string materialName;
        };
        
        // ID starts from 1, 0 means off.
        struct SmoothGroup
        {
            enum { SMOOTH_GRP_OFF = 0 };
            
            SmoothGroup(int groupId) : groupId(groupId) {}
            
            int groupId;
        };
        
        class ParserCallback
        {
            public:
                virtual ~ParserCallback() {}
                virtual void gotVertex     (Vertex      const&) = 0;
                virtual void gotTexCoord   (TexCoord    const&) = 0;
                virtual void gotNormal     (Normal      const&) = 0;
                virtual void gotFace       (Face        const&) = 0;
                virtual void gotMtlLib     (MtlLib      const&) = 0;
                virtual void gotObject     (Object      const&) = 0;
                virtual void gotGroup      (Group       const&) = 0;
                virtual void gotMaterial   (Material    const&) = 0;
                virtual void gotSmoothGroup(SmoothGroup const&) = 0;
        };
        
        struct IdLists
        {
            std::vector<int> vertexIds, texCoordIds, normalIds, faceIds;
        };
        
        class ObjectFile : protected ParserCallback
        {
            public:
                explicit ObjectFile(std::string const& filePath);
                
                std::vector<Vertex>   const& getVertexList()   const { return vertexList; }
                std::vector<TexCoord> const& getTexCoordList() const { return texCoordList; }
                std::vector<Normal>   const& getNormalList()   const { return normalList; }
                std::vector<Face>     const& getFaceList()     const { return faceList; }
                
            protected:
                virtual void gotVertex     (const obj_loader::Vertex& );
                virtual void gotTexCoord   (const obj_loader::TexCoord& );
                virtual void gotNormal     (const obj_loader::Normal& );
                virtual void gotFace       (const obj_loader::Face& );
                virtual void gotMtlLib     (const obj_loader::MtlLib& );
                virtual void gotObject     (const obj_loader::Object& );
                virtual void gotGroup      (const obj_loader::Group& );
                virtual void gotMaterial   (const obj_loader::Material& );
                virtual void gotSmoothGroup(const obj_loader::SmoothGroup& );
                
            private:
                std::vector<Vertex>   vertexList;
                std::vector<TexCoord> texCoordList;
                std::vector<Normal>   normalList;
                std::vector<Face>     faceList;
                
                struct CmpObj
                {
                    bool operator () (Object const& obj1, Object const& obj2) const
                    { return obj1.objectName < obj2.objectName; }
                };
                
                struct CmpGrp
                {
                    bool operator () (Group const& grp1, Group const& grp2) const
                    { return grp1.groupName < grp2.groupName; }
                };
                
                struct CmpMtl
                {
                    bool operator () (Material const& mtl1, Material const& mtl2) const
                    { return mtl1.materialName < mtl2.materialName; }
                };
                
                struct CmpSmth
                {
                    bool operator () (SmoothGroup const& smth1, SmoothGroup const& smth2) const
                    { return smth1.groupId < smth2.groupId; }
                };
                
                std::map<Object,      IdLists, CmpObj> objects;
                std::map<Group,       IdLists, CmpGrp> groups;
                std::map<Material,    IdLists, CmpMtl> materials;
                std::map<SmoothGroup, IdLists, CmpSmth> smoothGroups;
                
                Object currentObject;
                Group currentGroup;
                Material currentMaterial;
                SmoothGroup currentSmoothGroup;
        };
    }
    typedef obj_loader::ObjectFile ObjectFile;
}

#endif
